bitkeeper revision 1.1108.29.1 (4107987b3YS7DchKHz1bWQKqXsXTsg)
authortw275@labyrinth.cl.cam.ac.uk <tw275@labyrinth.cl.cam.ac.uk>
Wed, 28 Jul 2004 12:13:47 +0000 (12:13 +0000)
committertw275@labyrinth.cl.cam.ac.uk <tw275@labyrinth.cl.cam.ac.uk>
Wed, 28 Jul 2004 12:13:47 +0000 (12:13 +0000)
Added xensv command: xensv start / stop -
checks for twisted and for xend
More work on wizzard, now persits values and can move back / forth

.rootkeys
BitKeeper/etc/ignore
tools/misc/Makefile
tools/misc/xensv [new file with mode: 0755]
tools/python/xen/sv/CreateDomain.py
tools/python/xen/sv/Daemon.py [new file with mode: 0644]
tools/python/xen/sv/HTMLBase.py
tools/python/xen/sv/Wizzard.py
tools/python/xen/sv/params.py [new file with mode: 0644]
tools/python/xen/sv/util.py

index d09ef0e183218f45a015c479c6d5fe644ef8f49d..671bb2c25efc4e419a2004c1d33b968bda3f1f84 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3f870808zS6T6iFhqYPGelroZlVfGQ tools/misc/xen_cpuperf.c
 405eedf6_nnNhFQ1I85lhCkLK6jFGA tools/misc/xencons
 40c9c4697z76HDfkCLdMhmaEwzFoNQ tools/misc/xend
+4107986eMWVdBoz4tXYoOscpN_BCYg tools/misc/xensv
 4056f5155QYZdsk-1fLdjsZPFTnlhg tools/misc/xensymoops.py
 40cf2937dqM1jWW87O5OoOYND8leuA tools/misc/xm
 40c9c468icGyC5RAF1bRKsCXPDCvsA tools/python/Makefile
 40dc4076pVeE1kEEWzcUaNZin65kCA tools/python/xen/lowlevel/xu/domain_controller.h
 40dc4076CwBYRTUQDdbdU1L6KcLgSw tools/python/xen/lowlevel/xu/xu.c
 41052eb84_irpx0E9N_kqBp9eoin5g tools/python/xen/sv/CreateDomain.py
+4107986egkTAMIHW7n-i4ShvCGWpLQ tools/python/xen/sv/Daemon.py
 40fcefb2qm13BbRZBydAatOavaS0fQ tools/python/xen/sv/DomInfo.py
 40fcefb2-RIU8GB67mJMRzybME9bxw tools/python/xen/sv/DomList.py
 40fcefb23FfQn-ZBCbcHqA0cPGqQxw tools/python/xen/sv/GenTabbed.py
 40fcefb2Sif__6AqrANeBQZZfvP-6w tools/python/xen/sv/TabView.py
 41052eb8UrgtUkuJPg7oY1tutVQHsg tools/python/xen/sv/Wizzard.py
 40fcefb2DqteqCCZYDCvvh4Q5jBd0w tools/python/xen/sv/__init__.py
+4107986e6qN1IdvIDdId0AYFmDMkiQ tools/python/xen/sv/params.py
 40fcefb4rnaZNjqsBu7A5V2rlLyqRw tools/python/xen/sv/util.py
 40d8915cyoVA0hJxiBFNymL7YvDaRg tools/python/xen/util/Brctl.py
 40dfd40aGqGkiopOOgJxSF4iCbHM0Q tools/python/xen/util/__init__.py
index 60cc1f1a44142f0ff191cc34649b0c221c2ac92b..46e5aff70246049de32d7d3c39cc0a540df0e108 100644 (file)
@@ -35,3 +35,14 @@ xen/xen.*
 tools/xfrd/xfrd
 xen/tools/elf-reloc
 xen/tools/figlet/figlet
+docs/interface.aux
+docs/interface.log
+docs/interface.pdf
+docs/interface.ps
+docs/interface.toc
+docs/user.aux
+docs/user.log
+docs/user.pdf
+docs/user.ps
+docs/user.toc
+tools/web-shutdown.tap
index 979599736411414b71ae857193a1a0da56b2c73b..2f613ea17215d76769b308adfc84fa3bdb902eda 100644 (file)
@@ -21,7 +21,7 @@ OBJS     = $(patsubst %.c,%.o,$(SRCS))
 TARGETS  = xen_cpuperf
 
 INSTALL_BIN  = $(TARGETS) xencons
-INSTALL_SBIN = netfix xm xend
+INSTALL_SBIN = netfix xm xend xensv
 
 all: $(TARGETS)
        $(MAKE) -C miniterm
diff --git a/tools/misc/xensv b/tools/misc/xensv
new file mode 100755 (executable)
index 0000000..b877538
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+#  -*- mode: python; -*-
+#============================================================================
+# Copyright (C) 2004 Tom Wilkie <tw275@cl.cam.ac.uk>
+# Copyright (C) 2004 Mike Wray  <mike.wray@hp.com>
+#============================================================================
+
+"""SV web interface Lives in /usr/sbin.
+   Provides pretty HTML management interface.
+
+   Run:
+
+   sv start
+
+   The daemon is stopped with:
+
+   sv stop
+   
+   The daemon will be accessible from http://localhost:8080/
+"""
+import os
+import sys
+import re
+
+from xen.xend.server.params import PID_FILE as XEND_PID_FILE
+
+class CheckError(ValueError):
+    pass
+
+def hline():
+    print >>sys.stderr, "*" * 70
+
+def msg(message):
+    print >>sys.stderr, "*" * 3, message
+
+def check_logging():
+    """Check python logging is installed and raise an error if not.
+    Logging is standard from Python 2.3 on.
+    """
+    try:
+        import logging
+    except ImportError:
+        hline()
+        msg("Python logging is not installed.")
+        msg("Use 'make install-logging' at the xen root to install.")
+        msg("")
+        msg("Alternatively download and install from")
+        msg("http://www.red-dove.com/python_logging.html")
+        hline()
+        raise CheckError("logging is not installed")
+
+def check_twisted_version():
+    """Check twisted is installed with a supported version and print a warning if not.
+    Raises an error if twisted is not installed.
+    """
+    # Supported twisted release and major version.
+    RELEASE = 1
+    MAJOR   = 3
+    try:
+        from twisted.copyright import version
+    except ImportError:
+        hline()
+        msg("The Twisted framework is not installed.")
+        msg("Use 'make install-twisted' at the xen root to install.")
+        msg("")
+        msg("Alternatively download and install version %d.%d or higher" % (RELEASE, MAJOR))
+        msg("from http://www.twistedmatrix.com/products")
+        hline()
+        raise CheckError("twisted is not installed")
+        
+    
+    (release, major, minor) = version.split('.')
+    release = int(release)
+    major = int(major)
+    if release > RELEASE: return
+    if release == RELEASE and major >= MAJOR: return
+    hline()
+    msg("Warning: Twisted version not supported: %s" % version)
+    msg("Use Twisted version %d.%d.0 or higher" % (RELEASE, MAJOR))
+    hline()
+    
+def check_xend():
+    """Check xend is running
+    """
+    
+    if not os.path.isfile(XEND_PID_FILE) or not os.path.getsize(XEND_PID_FILE):
+       hline()
+        msg( "Warning: Xend has not been detected as running." )
+        msg( "Please start it immediately with: xend start " )
+        hline()
+        return 0
+        
+    # Read the pid of the previous invocation and search active process list.
+    pid = open(XEND_PID_FILE, 'r').read()
+    lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
+    for line in lines:
+        if re.search('^ *' + pid + '.+xend', line):
+            return 1
+
+    hline()
+    msg( "Warning: Xend has not been detected as running." )
+    msg( "Please start it immediately with: xend start " )
+    hline() 
+    return 0
+    
+def main():
+    try:
+        check_logging()
+        check_twisted_version()
+        check_xend()
+    except CheckError:
+        sys.exit(1)
+    
+    from xen.sv import Daemon
+
+    daemon = Daemon.instance()
+    
+    if not sys.argv[1:]:
+        print 'usage: %s {start|stop|restart}' % sys.argv[0]
+    elif os.fork():
+        pid, status = os.wait()
+        return status >> 8
+    elif sys.argv[1] == 'start':
+        return daemon.start()
+    elif sys.argv[1] == 'stop':
+        return daemon.stop()
+    elif sys.argv[1] == 'restart':
+        return daemon.stop() or daemon.start()
+    else:
+        print 'not an option:', sys.argv[1]
+    return 1
+
+if __name__ == '__main__':
+    sys.exit(main())
index 13c45d862241a9cfcfea2b4abaa45fff2fb31eea..62c6fe9d5b234c9aebe1441db0b49d4a8189230b 100644 (file)
@@ -14,30 +14,33 @@ class CreatePage0( Sheet ):
 
     def __init__( self, urlWriter ):
     
-       feilds = [( 'name', 'Name')]
+       feilds = [( 'name', 'VM Name:'),
+                  ( 'memory', 'RAM (Mb):' )]
     
-        Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 1" )
+        Sheet.__init__( self, urlWriter, feilds, "Create New Domain", 0 )
                 
 class CreatePage1( Sheet ):
 
     def __init__( self, urlWriter ):
     
-       feilds = [( 'name', 'Name')]
+       feilds = [( 'kernel_type', 'Kernel Type:'),
+                  ( 'kernel_location', 'Kernel location:')]
         
-        Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 2" )
+        Sheet.__init__( self, urlWriter, feilds, "Setup Kernel Image", 1 )
         
 class CreatePage2( Sheet ):
 
     def __init__( self, urlWriter ):
     
-       feilds = [( 'name', 'Name')]    
+       feilds = [( 'vbd_dom0', 'Location of vbd:'),
+                 ( 'vbd_dom0', 'Vitualised location:')]    
    
-        Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 3" )
+        Sheet.__init__( self, urlWriter, feilds, "Setup Virtual Block Devices", 2 )
                 
 class CreatePage3( Sheet ):
 
     def __init__( self, urlWriter ):
     
-       feilds = [( 'name', 'Name')]
+       feilds = [( 'vifs', 'Number of Vifs:')]
         
-        Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 4" )       
\ No newline at end of file
+        Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 4", 3 )       
diff --git a/tools/python/xen/sv/Daemon.py b/tools/python/xen/sv/Daemon.py
new file mode 100644 (file)
index 0000000..fdbc614
--- /dev/null
@@ -0,0 +1,108 @@
+###########################################################
+## XenSV Web Control Interface Daemon
+## Copyright (C) 2004, K A Fraser (University of Cambridge)
+## Copyright (C) 2004, Mike Wray <mike.wray@hp.com>
+## Copyright (C) 2004, Tom Wilkie <tw275@cam.ac.uk>
+###########################################################
+
+import os
+import os.path
+import sys
+import re
+
+from xen.sv.params import *
+
+from twisted.internet import reactor
+from twisted.web import static, server, script
+
+class Daemon:
+    """The xend daemon.
+    """
+    def __init__(self):
+        self.shutdown = 0
+        self.traceon = 0
+
+    def daemon_pids(self):
+        pids = []
+        pidex = '(?P<pid>\d+)'
+        pythonex = '(?P<python>\S*python\S*)'
+        cmdex = '(?P<cmd>.*)'
+        procre = re.compile('^\s*' + pidex + '\s*' + pythonex + '\s*' + cmdex + '$')
+        xendre = re.compile('^/usr/sbin/xend\s*(start|restart)\s*.*$')
+        procs = os.popen('ps -e -o pid,args 2>/dev/null')
+        for proc in procs:
+            pm = procre.match(proc)
+            if not pm: continue
+            xm = xendre.match(pm.group('cmd'))
+            if not xm: continue
+            #print 'pid=', pm.group('pid'), 'cmd=', pm.group('cmd')
+            pids.append(int(pm.group('pid')))
+        return pids
+
+    def new_cleanup(self, kill=0):
+        err = 0
+        pids = self.daemon_pids()
+        if kill:
+            for pid in pids:
+                print "Killing daemon pid=%d" % pid
+                os.kill(pid, signal.SIGHUP)
+        elif pids:
+            err = 1
+            print "Daemon already running: ", pids
+        return err
+            
+    def cleanup(self, kill=False):
+        # No cleanup to do if PID_FILE is empty.
+        if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE):
+            return 0
+        # Read the pid of the previous invocation and search active process list.
+        pid = open(PID_FILE, 'r').read()
+        lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
+        for line in lines:
+            if re.search('^ *' + pid + '.+xensv', line):
+                if not kill:
+                    print "Daemon is already running (pid %d)" % int(pid)
+                    return 1
+                # Old daemon is still active: terminate it.
+                os.kill(int(pid), 1)
+        # Delete the stale PID_FILE.
+        os.remove(PID_FILE)
+        return 0
+
+    def start(self, trace=0):
+        if self.cleanup(kill=False):
+            return 1
+   
+        # Fork -- parent writes PID_FILE and exits.
+        pid = os.fork()
+        if pid:
+            # Parent
+            pidfile = open(PID_FILE, 'w')
+            pidfile.write(str(pid))
+            pidfile.close()
+            return 0
+        # Child
+        self.run()
+        return 0
+
+    def stop(self):
+        return self.cleanup(kill=True)
+
+    def run(self):
+       root = static.File( SV_ROOT )
+        root.indexNames = [ 'Main.rpy' ]
+        root.processors = { '.rpy': script.ResourceScript }
+        reactor.listenTCP( SV_PORT, server.Site( root ) )
+        reactor.run()
+
+    def exit(self):
+        reactor.diconnectAll()
+        sys.exit(0)
+
+def instance():
+    global inst
+    try:
+        inst
+    except:
+        inst = Daemon()
+    return inst
index 7c500e8a2b93101fe679d1191173743fd75bf393..07adfab76daff9f5626fd3a968abe88bcef3067a 100755 (executable)
@@ -25,9 +25,9 @@ class HTMLBase( Resource ):
         request.write( '<html><head><title>Xen</title><link rel="stylesheet" type="text/css" href="inc/style.css" />' )
         request.write( '<script src="inc/script.js"></script>' )
         request.write( '</head><body>' )
+        request.write('<form method="post" action="%s">' % request.uri)
 
     def write_BOTTOM( self, request ):
-        request.write('<form method="post" action="%s">' % request.uri)
         request.write('<input type="hidden" name="op" value="">')
         request.write('</form>')
         request.write( "</body></html>" )
index 0f506d03b825ff1f6c8c6bf83b2489b432837604..eaec7592b625c1002ec886da319f2e70a7593f4d 100755 (executable)
@@ -2,13 +2,14 @@ from xen.sv.util import *
 from xen.sv.HTMLBase import HTMLBase
 from xen.xend import sxp
 
+DEBUG = 1
+
 class Wizzard( HTMLBase ):
 
     def __init__( self, urlWriter, title, sheets ):
         HTMLBase.__init__( self )
         self.title = title
         self.sheets = sheets
-        self.currSheet = 0
         self.urlWriter = urlWriter
         
     def write_MENU( self, request ):
@@ -22,45 +23,90 @@ class Wizzard( HTMLBase ):
         currSheet = getVar( 'sheet', request )
     
         if not currSheet is None:
+            currSheet = int( currSheet )
+        else:
+            currSheet = 0
+            
+        op = getVar( 'op', request )
         
-            self.currSheet = int( currSheet )
+        if op == 'next':
+            currSheet += 1
+        elif op == 'prev':
+            currSheet -= 1    
             
-        self.sheets[ self.currSheet ]( self.urlWriter ).write_BODY( request )
+        self.sheets[ currSheet ]( self.urlWriter ).write_BODY( request )
         
         request.write( "</td></tr><tr><td><table width='100%' border='0' cellspacing='0' cellpadding='0'><tr>" )
-        request.write( "<td width='80%'></td><td width='20%' align='center'>" )
-        request.write( "<p align='center'><img src='images/previous.png' onclick='doOp( \"prev\" )' onmouseover='update( \"wizText\", \"Previous\" )' onmouseout='update( \"wizText\", \"&nbsp;\" )'>&nbsp;" )
-        request.write( "<img src='images/next.png' onclick='doOp( \"next\" )' onmouseover='update( \"wizText\", \"Next\" )' onmouseout='update( \"wizText\", \"&nbsp;\" )'></p>" )
-        request.write( "<p align='center'><span id='wizText'></span></p></td></tr></table>" )
+        request.write( "<td width='80%'></td><td width='20%' align='center'><p align='center'>" )
+       if currSheet > 0:
+                       request.write( "<img src='images/previous.png' onclick='doOp( \"prev\" )' onmouseover='update( \"wizText\", \"Previous\" )' onmouseout='update( \"wizText\", \"&nbsp;\" )'>&nbsp;" )
+        if currSheet < ( len( self.sheets ) - 1 ):        
+               request.write( "<img src='images/next.png' onclick='doOp( \"next\" )' onmouseover='update( \"wizText\", \"Next\" )' onmouseout='update( \"wizText\", \"&nbsp;\" )'>" )
+        request.write( "</p><p align='center'><span id='wizText'></span></p></td></tr></table>" )
         request.write( "</td></tr></table>" )
         
+    def op_next( self, request ):
+       pass
+        
+    def op_prev( self, request ):
+       pass  
+        
 class Sheet( HTMLBase ):
 
-    def __init__( self, urlWriter, feilds, title ):
+    def __init__( self, urlWriter, feilds, title, location ):
         HTMLBase.__init__( self )
         self.urlWriter = urlWriter
         self.feilds = feilds
         self.title = title
+        self.location = location
+        self.passback = "()"
         
     def parseForm( self, request ):
-       return sxp.toString( request.args )
+       do_not_parse = [ 'mod', 'op', 'sheet' ] 
+    
+       passed_back = request.args
+        
+        temp_passback = passed_back.get( "passback" )
+        
+        if temp_passback is not None and len( temp_passback ) > 0:
+            temp_passback = temp_passback[ len( temp_passback )-1 ]
+        else:
+            temp_passback = "(passback )"        
+        
+        last_passback = ssxp2hash( string2sxp( temp_passback ) )
+        
+        if DEBUG: print last_passback
+        
+        try: 
+            del passed_back[ 'passback' ]
+        except:
+            pass
+        
+        for (key, value) in passed_back.items():
+            if key not in do_not_parse:
+                last_passback[ key ] = value[ len( value ) - 1 ]
+                
+        self.passback = sxp2string( hash2sxp( last_passback ) ) #store the sxp
+        
+        if DEBUG: print self.passback
+        
+        return last_passback #return the hash
         
     def write_BODY( self, request ):
+        
        request.write( "<p>%s</p>" % self.title )
     
-       previous_values = request.args
+       previous_values = self.parseForm( request ) #get the hash for quick reference
         
        for (feild, name) in self.feilds:
-            value = sxp.child_value( previous_values, feild )
+            value = previous_values.get( feild )
             if value is None:
                value = ''
             request.write( "<p>%s<input type='text' name='%s' value='%s'></p>" % (name, feild, value) )
-            
-    def op_next( self, request ):
-       pass
         
-    def op_prev( self, request ):
-       pass        
+        request.write( "<input type='hidden' name='passback' value=\"%s\"></p>" % self.passback )
+        request.write( "<input type='hidden' name='sheet' value='%s'></p>" % self.location )        
+                 
             
     
       
diff --git a/tools/python/xen/sv/params.py b/tools/python/xen/sv/params.py
new file mode 100644 (file)
index 0000000..16b764f
--- /dev/null
@@ -0,0 +1,3 @@
+SV_PORT = 8080
+SV_ROOT = "/var/xen/sv/"
+PID_FILE = "/var/xen/sv.pid"
\ No newline at end of file
index adf75b073cf44b7066908bebc990786c561dadc4..c14137609f64a4ee4fb83cf0f2008ab53aa34dcf 100755 (executable)
@@ -18,7 +18,32 @@ def sxp2hash( s ):
         else:
             sxphash[ child[0] ] = child[1]
         
-    return sxphash   
+    return sxphash  
+    
+def ssxp2hash( s ):
+    sxphash = {}
+    
+    for i in s:
+       if isinstance( i, types.ListType ) and len( i ) > 1:
+          sxphash[ i[0] ] = i[1]
+    
+    return sxphash 
+    
+def hash2sxp( h ):
+    hashsxp = []
+    
+    for (key, item) in h.items():
+       hashsxp.append( [key, item] )
+        
+    return hashsxp    
+    
+def string2sxp( string ):
+    pin = sxp.Parser()
+    pin.input( string )
+    return pin.get_val()    
+
+def sxp2string( sexp ):
+    return sxp.to_string( sexp )    
     
 def sxp2prettystring( sxp ):
     class tmp: